package com.yuqian.talkservers;


import com.google.gson.Gson;
import com.yuqian.bean.Message;
import com.yuqian.bean.OffOnlineMessage;
import com.yuqian.bean.TalkingUser;
import com.yuqian.bean.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.net.ConnectionResetException;
//import servers.OffOnlineMessageRest;

import javax.swing.*;
import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 *
 */

public class TCPServer {


    Logger logger= LoggerFactory.getLogger(TCPServer.class);
    /**
     * 逻辑规划
     * 1.由一个HashMap来负责储存所有的群聊信息
     * 2.（登录）当某一个用户登录时，如果此时只有其一个用户，需要先从数据库获取到全部群的所有用户
     * 通过用户数据接口返回给用户，再与用户本地的数据进行匹配
     * 3.（消息）消息类型划分 （1.普通内容，指的是文字  （2.图片 3.图文混排 初步计划使用HTML （4.通知（a.上线下线 b.系统通知）
     * （5.公告
     * （6.审核消息，需要格式化处理
     * 4.（接收数据）客户端只需要维护一个TCPclient,根据服务器返回的from to 以及消息类型来对服务器来的消息进行分发
     * 本地的显示
     * 5.用户头像储存在数据库，通过用户数据接口发送给各个用户
     * 6.当用户数量从0-1时，服务器需启动该用户所存在的所有群服务
     * 7.当一个用户登录时，如果该用户存在的群聊超出现有的群聊服务中，则需要找到未启动的群聊将其启动
     * 8.离线消息的发送，当向一名用户发送消息，但是该用户不在时，通过离线消息接口将消息储存在数据库中
     * 9.用户刚登陆时，第一件事是通过离线接口获取消息状态为未读的消息，当在客户端点开后，将所有未读消息状态
     * 设置为已读。
     * 10.用户这部分需要有多设备的记录。
     * <p>
     * ************************************数据库对聊天数据的存放*******************************************************
     * 1.用户聊天，并非每条消息都第一时间存放在数据库，而是在用户端进行聊天记录的本地储存以及数据库的同步
     * 2.群聊只有一张表，保存群聊id，用户id，
     * 3.聊天记录
     */
    String msgString = null;
    Gson gson = new Gson();
    private ServerSocket serverSocket;
    private TreeMap<String, TreeMap<String, TalkingUser>> groupCharts;//群聊列表
    /**
     * 创建线程池来管理客户端的连接线程
     * 避免系统资源过度浪费
     */
    private ExecutorService exec;

    // 存放客户端之间私聊的信息  
    private Map<String, PrintWriter> storeInfo;

    public TCPServer() {
        try {

            serverSocket = new ServerSocket(6789);
            storeInfo = new HashMap<String, PrintWriter>();
            exec = Executors.newCachedThreadPool();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 将客户端的信息以Map形式存入集合中  
    private void putIn(String key, PrintWriter value) {
        synchronized (this) {
            storeInfo.put(key, value);
        }
    }


    // 将给定的消息转发给所有客户端
    private synchronized void sendToAll(String message) {
        for (PrintWriter out : storeInfo.values()) {
            out.println(message);
        }
    }

    // 将给定的消息转发给私聊的客户端  
    private synchronized void sendToSomeone(String name, String message) {
        PrintWriter pw = storeInfo.get(name); //将对应客户端的聊天信息取出作为私聊内容发送出去  
        if (pw != null) pw.println(message);
    }

    public void start() {
        try {
            while (true) {
                System.out.println("等待客户端连接... ... ");
                Socket socket = serverSocket.accept();

                // 获取客户端的ip地址
                InetAddress address = socket.getInetAddress();
                System.out.println("客户端：“" + address.getHostAddress() + "”连接成功！ ");

            /* 
            * 启动一个线程，由线程来处理客户端的请求，这样可以再次监听 
            * 下一个客户端的连接 
            */
                exec.execute(new ListenrClient(socket)); //通过线程池来分配线程
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 该线程体用来处理给定的某一个客户端的消息，循环接收客户端发送
     * 的每一个字符串，并输出到控制台
     */
    class ListenrClient implements Runnable {

        private Socket socket;
        private String name;

        public ListenrClient(Socket socket) {
            this.socket = socket;
        }

        // 创建内部类来获取昵称  
        private String getName() throws Exception {
            try {
                //服务端的输入流读取客户端发送来的昵称输出流  
                BufferedReader bReader = new BufferedReader(
                        new InputStreamReader(socket.getInputStream(), "UTF-8"));
                //服务端将昵称验证结果通过自身的输出流发送给客户端  
                PrintWriter ipw = new PrintWriter(
                        new OutputStreamWriter(socket.getOutputStream(), "UTF-8"), true);
                out("输出输出流建立完成");
                //读取客户端发来的昵称  
                while (true) {
                    out("循环监听");
                    ipw.println("OK");
                    //    String nameString = bReader.readLine();
                    //     if ((nameString.trim().length() == 0) || storeInfo.containsKey(nameString)) {
                    //        ipw.println("FAIL");
                    ///   } else {
                    //      ipw.println("OK");
                    //      return nameString;

                    //  }
                    return "";
                }
            } catch (Exception e) {
                out("服务器监听客户端异常");
                throw e;
            }
        }


        public void run() {
            try {  
                /* 
                * 通过客户端的Socket获取客户端的输出流 
                * 用来将消息发送给客户端 
                */
                PrintWriter pw = new PrintWriter(
                        new OutputStreamWriter(socket.getOutputStream(), "UTF-8"), true);
                //  getName();
                /* 
                * 将客户昵称和其所说的内容存入共享集合HashMap中 
                */
                //    name = getName();
                //       putIn(name, pw);
                //      Thread.sleep(100);

                // 服务端通知所有客户端，某用户上线  
                //     sendToAll("[系统通知] “" + name + "”已上线");
                  
                /* 
                * 通过客户端的Socket获取输入流 
                * 读取客户端发送来的信息 
                */
                BufferedReader bReader = new BufferedReader(
                        new InputStreamReader(socket.getInputStream(), "UTF-8"));


                Message login = null;


                //  out("读取到"+bReader.readLine());
                while ((msgString = bReader.readLine()) != null) {


                logger.info(String.valueOf(gson.fromJson(msgString,Message.class).getId()));
                    login = gson.fromJson(msgString, Message.class);
                    //  login= JSON.parseObject(msgString,Login.class);

                  //  System.out.println("获取到消息" + msgString);
                 //   System.out.println(login.getContent());
                    if (login.getMessage().equals("login")) {
                        Message officeOnlineMessage = new Message();
                        officeOnlineMessage.setTowhere(String.valueOf(login.getId()));
                        officeOnlineMessage.setRecode("1");
                        officeOnlineMessage.setFromwhere("system");
                        //登录操作逻辑
                       //  User user= JSON.toJavaObject(json,User.class);
                        User user = new User();
                        user.setId(Long.valueOf(login.getId()));
                        if (UserUtils.hasUser(user)) {
                            logger.info("存在此用户");
                            TalkingUser userOnline = UserUtils.getUser(String.valueOf(user.getId()));
                            if (login.getDeviceType().equals("PC")) {
                                if (userOnline.isDevice1Online()) {
                                    officeOnlineMessage.setMessage("offOnline");
                                    officeOnlineMessage.setContent("您的账号在其他设备登录，您已下线,即将退出程序");
                                    userOnline.getDevice1().println(gson.toJson(officeOnlineMessage));
                                    userOnline.getDevice1().close();
                                    UserUtils.removePortWithUser(userOnline.getDevice1Port());
                                    userOnline.setDevice1(pw);
                                    userOnline.setDevice1Online(true);
                                    userOnline.setDevice1Port(String.valueOf(socket.getPort()));
                                    UserUtils.sendToAll("用户" + login.getId() + "的PC设备已上线");
                                } else {
                                    userOnline.setDevice1(pw);
                                    userOnline.setDevice1Online(true);
                                    userOnline.setDevice1Port(String.valueOf(socket.getPort()));
                                    officeOnlineMessage.setMessage("userOnline");
                                    officeOnlineMessage.setDeviceType("PC");
                                    officeOnlineMessage.setContent("用户" + login.getId() + "的PC设备已上线");
                                    UserUtils.sendToAll(gson.toJson(officeOnlineMessage));
                                }
                            } else if (login.getDeviceType().equals("Phone")) {
                                if (userOnline.isDevice2Online()) {

                                    officeOnlineMessage.setMessage("offOnline");
                                    officeOnlineMessage.setContent("您的账号在其他设备登录，您已下线");
                                    officeOnlineMessage.setFromwhere("system");
                                    officeOnlineMessage.setTowhere(userOnline.getUserId());
                                    officeOnlineMessage.setRecode("1");

                                    userOnline.getDevice2().println(gson.toJson(officeOnlineMessage));
                                    userOnline.getDevice2().close();
                                    UserUtils.removePortWithUser(userOnline.getDevice2Port());
                                    userOnline.setDevice2(pw);
                                    userOnline.setDevice2Online(true);
                                    userOnline.setDevice2Port(String.valueOf(socket.getPort()));
                                   UserUtils.sendToAll("用户" + login.getId() + "的移动设备已上线");
                                } else {
                                    userOnline.setDevice2(pw);
                                    userOnline.setDevice2Online(true);
                                    userOnline.setDevice2Port(String.valueOf(socket.getPort()));
                                    officeOnlineMessage.setMessage("userOnline");
                                    officeOnlineMessage.setDeviceType("Phone");
                                    officeOnlineMessage.setContent("用户" + login.getId() + "的移动设备已上线");
                                    UserUtils.sendToAll(gson.toJson(officeOnlineMessage));
                                }
                            }
                            UserUtils.userOnLine(userOnline, String.valueOf(socket.getPort()));
                        } else {
                            logger.info("不存在此用户");
                            TalkingUser newUser = new TalkingUser();
                            newUser.setUserId(String.valueOf(user.getId()));
                            if (login.getDeviceType().equals("PC")) {
                                logger.info("新建用户PC在线");
                                newUser.setDevice1(pw);
                                newUser.setDevice1Online(true);
                                newUser.setDevice1Port(String.valueOf(socket.getPort()));
                                UserUtils.userOnLine(newUser, String.valueOf(socket.getPort()));
                                officeOnlineMessage.setMessage("userOnline");
                                officeOnlineMessage.setDeviceType("PC");
                                officeOnlineMessage.setContent("用户" + login.getId() + "的PC设备已上线");
                                UserUtils.sendToAll(gson.toJson(officeOnlineMessage));
                                //System.out.println("用户" + login.getId() + "的" + login.getDeviceType() + "设备已上线");
                            } else if (login.getDeviceType().equals("Phone")) {
                                logger.info("新建用户手机在线");
                                newUser.setDevice2(pw);
                                newUser.setDevice2Online(true);
                                newUser.setDevice2Port(String.valueOf(socket.getPort()));
                                UserUtils.userOnLine(newUser, String.valueOf(socket.getPort()));
                                officeOnlineMessage.setMessage("userOnline");
                                officeOnlineMessage.setDeviceType("Phone");
                                officeOnlineMessage.setTowhere("all");
                                officeOnlineMessage.setContent("用户" + login.getId() + "的移动设备已上线");
                                UserUtils.sendToAll(gson.toJson(officeOnlineMessage));
                           //     System.out.println("用户" + login.getId() + "的" + login.getDeviceType() + "设备已上线");
                            }


                            //   out("新用户端口"+socket.getPort());
                            //out("当前在线人数" + UserUtils.getOnlineNum());
                            officeOnlineMessage.setMessage("systemmessage");
                            officeOnlineMessage.setContent("服务连接成功");
                            logger.info("向客户端发送的内容"+gson.toJson(officeOnlineMessage));
                            pw.println(gson.toJson(officeOnlineMessage));
                        }


                    } else if (login.getMessage().equals("message")) {

                        UserUtils.sendMessage(msgString);

                    } else if (login.getMessage().equals("loginout")) {
                       logger.info(login.getId()+"退出登录");
                    }


                    // 遍历所有输出流，将该客户端发送的信息转发给所有客户端  

                    //   sendToAll(name+"："+ msgString);
                }
            } catch (NullPointerException e) {

                //offOnlineMessage.setUserId();
                // e.printStackTrace();
                logger.info("出错了" + e + "端口号"+socket.getPort());


              //reponsitory.save(offOnlineMessage);
               // rest.save(msgString,gson);

            } catch (SocketException e){
                logger.error("用户已断开连接");

                UserUtils.sendToAll(UserUtils.offOnlineMessage(String.valueOf(socket.getPort())));
            } catch (UnsupportedEncodingException e) {
                logger.error("转码失败");
                e.printStackTrace();
            } catch (IOException e) {
                logger.error("输入输出异常");
                e.printStackTrace();
            } finally {
              //  out("出错了应该做什么");
              //  UserUtils.sendToAll(String.valueOf(socket.getPort()));

            }
        }
    }

    public static void out(String info) {
        System.out.println(info);
    }
}  